/**
 * @ShardedJedis2Pool.java  COPYRIGHT xiaojian
 * 
 * Created on 2019年7月29日 下午5:04:03
 */
package xj.toolkit.redis.shard;

import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.Hashing;
import redis.clients.util.Pool;
import xj.toolkit.redis.RedisPoolStaticticsMBean;

/**
 * @author <a href="mailto:cxj2000@gmail.com">xiaojian.cao</a>
 *
 */
public class ShardedJedis2Pool extends Pool<ShardedJedis2> {

	public ShardedJedis2Pool(final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards) {
		this(poolConfig, shards, Hashing.MURMUR_HASH);
	}

	public ShardedJedis2Pool(final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards, Hashing algo) {
		this(poolConfig, shards, algo, null);
	}

	public ShardedJedis2Pool(final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards,
			Pattern keyTagPattern) {
		this(poolConfig, shards, Hashing.MURMUR_HASH, keyTagPattern);
	}

	public ShardedJedis2Pool(final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards, Hashing algo,
			Pattern keyTagPattern) {
		super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));
	}

	public RedisPoolStaticticsMBean getRedisPoolStatictics() {
		return new RedisPoolStaticticsMBean(this.internalPool);
	}

	public void returnResource(final ShardedJedis2 resource) {
		if (resource != null) {
			returnResourceObject(resource);
		}
	}

	@Override
	public ShardedJedis2 getResource() {
		ShardedJedis2 jedis = super.getResource();
		jedis.setDataSource(this);
		return jedis;
	}

	public void returnBrokenResource(ShardedJedis2 jedis) {
		if (jedis == null) {
			return;
		}
		try {
			internalPool.invalidateObject(jedis);
		} catch (Exception e) {
			throw new JedisException("Could not return the broken resource to the pool", e);
		}
	}


	/**
	 * PoolableObjectFactory custom impl.
	 */
	private static class ShardedJedisFactory implements PooledObjectFactory<ShardedJedis2> {
		private List<JedisShardInfo> shards;
		private Hashing algo;
		private Pattern keyTagPattern;

		public ShardedJedisFactory(List<JedisShardInfo> shards, Hashing algo, Pattern keyTagPattern) {
			this.shards = shards;
			this.algo = algo;
			this.keyTagPattern = keyTagPattern;
		}

		@Override
		public PooledObject<ShardedJedis2> makeObject() throws Exception {
			ShardedJedis2 jedis = new ShardedJedis2(shards, algo, keyTagPattern);
			return new DefaultPooledObject<ShardedJedis2>(jedis);
		}

		@Override
		public void destroyObject(PooledObject<ShardedJedis2> pooledShardedJedis) throws Exception {
			final ShardedJedis shardedJedis = pooledShardedJedis.getObject();
			for (Jedis jedis : shardedJedis.getAllShards()) {
				if (jedis.isConnected()) {
					try {
						try {
							jedis.quit();
						} catch (Exception e) {

						}
						jedis.disconnect();
					} catch (Exception e) {

					}
				}
			}
		}

		@Override
		public boolean validateObject(PooledObject<ShardedJedis2> pooledShardedJedis) {
			try {
				ShardedJedis jedis = pooledShardedJedis.getObject();
				for (Jedis shard : jedis.getAllShards()) {
					if (!shard.ping().equals("PONG")) {
						return false;
					}
				}
				return true;
			} catch (Exception ex) {
				return false;
			}
		}

		@Override
		public void activateObject(PooledObject<ShardedJedis2> p) throws Exception {

		}

		@Override
		public void passivateObject(PooledObject<ShardedJedis2> p) throws Exception {

		}
	}
}
